#include <game/GameDefinition.h>
#include <game/Root.h>
#include <game/flow/Manager.h>
#include <game/flow/FlowDefinition.h>
#include <game/ResourceReferenceDefinition.h>
#include <game/debug/DebugService.h>
#include "services/TextServiceImpl.h"

#include <Gluck.h>
#include <xml/Stream.h>
#include <resource/Manager.h>

using namespace liba;
using namespace xml;

ARELLO_GAME_NAMESPACE_DECL_START

class WindowDefinition
	: public xml::ObjectANSI
{
public:
	virtual bool on_attribute( Provider * prov, const std::string & name, const std::string & value )
	{
		if (name == "Height")
			return converter::convert(value, &this->height);
		if (name == "Width")
			return converter::convert(value, &this->width);

		return false;
	}
public:
	WindowDefinition()
		: width(0)
		, height(0)
	{ }

	size_t width;
	size_t height;
};

class CursorDefinition
	: public xml::ObjectANSI
{
public:
	virtual bool on_node(Provider* prov, const std::string& name)
	{
		if (name == "Resource")
		{
			ResourceReferenceDefinition def;
			if (!prov->parse_object(def.GetLoader()))
				return false;

			this->path = def.GetPath();
			return true;
		}

		return false;
	}

	const filesystem::String& GetPath() { return path; }
private:
	filesystem::String path;
};

class StringsDefinition
	: public xml::ObjectANSI
{
public:
	StringsDefinition()
		: textService(new TextServiceImpl())
	{ }

	virtual bool on_node(xml::Provider * prov, const std::string & name)
	{
		if (name == "Resource")
		{
			ResourceReferenceDefinition def;

			if (!prov->parse_object(def.GetLoader()))
				return false;

			std::auto_ptr<resource::Resource> chapterResource(resource::Manager::create_resource(def.GetPath()));
			if (!this->textService->ReadFrom(chapterResource.get()))
				THROW(GluckBadResource(__FUNCTION__, "Text service couldn't parse resource", def.GetPath()));

			return true;
		}

		return false;
	}

	std::auto_ptr<TextServiceImpl> ReleaseTextService() { return textService; }
private:
	std::auto_ptr<TextServiceImpl> textService;
};

class DebugDefinition
	: public xml::ObjectANSI
{
public:
	virtual bool on_attribute( Provider * prov, const std::string & name, const std::string & value )
	{
		if (name == "Activate")
			return converter::convert(value, &this->activate);

		return false;
	}

	virtual bool on_node(Provider* prov, const std::string& name)
	{
		if (name == "Resource")
		{
			ResourceReferenceDefinition def;
			if (!prov->parse_object(def.GetLoader()))
				return false;

			this->path = def.GetPath();
			return true;
		}

		return false;
	}

	const filesystem::String& GetPath() { return path; }
public:
	DebugDefinition()
		: activate(false)
	{ }

	bool activate;

private:
	filesystem::String path;
};

GameDefinition::GameDefinition() 
	: loadingRoot(liba::Atom("Loading::Root"))
	, debugActivate(false)
{
}

GameDefinition::~GameDefinition()
{
}

bool GameDefinition::on_node(xml::Provider * prov, const std::string & name)
{
	if (name == "Window")
	{
		WindowDefinition window;
		if (!prov->parse_object(&window))
			return false;

		if (!window.width || !window.height)
			THROW(Gluck(__FUNCTION__, "Screen node in game properties should specify both width and height"));

		this->width = window.width;
		this->height = window.height;

		return true;
	}
	if (name == "ScreenFlow")
	{
		flow::FlowDefinition def;
		if (!prov->parse_object(def.GetLoader()))
			return false;

		this->flowManager = def.ReleaseFlowManager();
		return true;
	}
	if (name == "Strings")
	{
		StringsDefinition def;

		if (!prov->parse_object(&def))
			return false;

		this->predefinedServices.Register(std::auto_ptr<services::Text>(def.ReleaseTextService().release()));
		return true;
	}
	if (name == "Cursor")
	{
		CursorDefinition def;
		if (!prov->parse_object(&def))
			return false;

		this->cursorScenePath = def.GetPath();
		return true;
	}
	if (name == "Debug")
	{
		DebugDefinition debug;

		if (!prov->parse_object(&debug))
			return false;

		/*if (!debug.activate)
			THROW(Gluck(__FUNCTION__, "Screen node in game properties should specify both width and height"));*/

		this->debugActivate = debug.activate;
		this->debugScenePath = debug.GetPath();
		return true;
	}

	return false;
}

std::auto_ptr<Root> GameDefinition::CreateRoot(liba::lib3d::Application* app)
{
	std::auto_ptr<Root> localGame(new Root(app));
	localGame->MergeFrom(this->predefinedServices);
	
	if (config::ENABLE_DEBUG_SERVICES)
	{
		localGame->Register(
			std::auto_ptr<debug::DebugService>(new debug::DebugService()));
	}

	localGame->SetBackendFactory(this->backendFactory);
	localGame->SetSelectorFactory(this->selectorFactory);
	localGame->SetTriggerFactory(this->triggerFactory);
	localGame->SetControlFactory(this->controlFactory);
	localGame->SetFlowManager(this->flowManager);

	if(this->debugActivate)
	{
		std::auto_ptr<game::DebugInfoManager> debug;
		debug.reset(new DebugInfoManager());
		liba::lib3d::node::Text text(Atom(),0);
		
		std::auto_ptr<resource::Resource> loadingResource(resource::Manager::create_resource(debugScenePath));
		if(loadingResource.get() == NULL)
			throw GluckBadResource(__FUNCTION__, "Failed to load", debugScenePath);

		if(!xml::StreamResource(loadingResource.get()).load(&xml::Root(&loadingRoot, "Scene")))
			throw GluckBadResource(__FUNCTION__, "Parse failed", "data/loading.scene");

		debug->SetCamera(loadingRoot.find_camera(Atom("Camera")));

		debug->SetFPSNode(loadingRoot.FindNode<lib3d::node::Text>(Atom("FPS"), true));

		localGame->SetDebugManager(debug);	
	}

	return localGame;
}

ARELLO_GAME_NAMESPACE_DECL_END